package com.tianji.learning.utils;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.tianji.learning.domain.po.LearningLesson;
import com.tianji.learning.domain.po.LearningRecord;
import com.tianji.learning.mapper.LearningLessonMapper;
import com.tianji.learning.mapper.LearningRecordMapper;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.ApplicationArguments;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.DelayQueue;

@Data
@Slf4j
@Component
@RequiredArgsConstructor
public class LearningRecordDelayTaskHandler {
    private final StringRedisTemplate redisTemplate;
    private final String REDIS_KEY_PREFIX = "learning:record:{}";
    private DelayQueue<DelayTask<delayTaskData>> delayQueue = new DelayQueue<>();
    private final LearningRecordMapper recordMapper;
    private final LearningLessonMapper lessonMapper;
    private static Boolean flag = true;

    @PostConstruct
    public void init(){
        log.info("Bean初始化阶段执行，开启一个新的线程执行延时检测任务...");
        //new Thread(() -> handleDelayTask()).start();
        CompletableFuture.runAsync(() -> handleDelayTask());
    }

    @PreDestroy
    public void destroy(){
        log.info("Bean销毁阶段执行，停止执行延时检测任务...");
        flag = false;
    }

    /**
     * 从缓存中读取学习记录
     *
     * @param lessonId
     * @param sectionId
     * @return
     */
    public LearningRecord readLearningRecordToCache(Long lessonId, Long sectionId) {
        log.info("从缓存中读取学习记录，lessonId:{},sectionId:{}", lessonId, sectionId);
        //1.组装大Key
        String key = String.format(REDIS_KEY_PREFIX, lessonId);
        //2.组装HashKey
        String hashKey = sectionId.toString();
        //3.发起查询
        String hashValue = (String) redisTemplate.opsForHash().get(key, hashKey);
        if (ObjectUtil.isEmpty(hashValue)) {
            log.info("缓存中未查询到学习记录，lessonId:{},sectionId:{}", lessonId, sectionId);
            return null;
        }
        //4.封装对象
        LearningRecord learningRecord = JSONUtil.toBean(hashValue, LearningRecord.class);
        //5.返回
        return learningRecord;
    }

    /**
     * 将学习记录写入缓存
     *
     * @param learningRecord : 旧记录
     * @return
     */
    public void writeLearningRecordToCache(LearningRecord learningRecord) {
        log.info("向缓存中写入学习记录，学习记录数据：{}", learningRecord);
        //1.组装大Key
        String key = String.format(REDIS_KEY_PREFIX, learningRecord.getLessonId());
        //2.组装HashKey
        String hashKey = learningRecord.getSectionId().toString();
        //组装hashValue
        String hashValue = JSONUtil.toJsonStr(new RedisRecordData(learningRecord));
        //3.发起查询
        redisTemplate.opsForHash().put(key, hashKey, hashValue);
        redisTemplate.expire(key, Duration.ofMinutes(1));

    }


    /**
     * 将学习记录暂存至缓存
     * 提交延时任务
     *
     * @param learningRecord
     */
    public void addLearningRecordDelayCheckTask(LearningRecord learningRecord) {
        log.info("提交学习记录，学习记录数据：{}", learningRecord);
        writeLearningRecordToCache(learningRecord);
        delayQueue.add(new DelayTask<>(Duration.ofMinutes(20), new delayTaskData(learningRecord)));

    }

    /**
     * 删除学习记录的延时任务
     *
     * @param lessonId
     * @param sectionId
     */
    public void deleteLearningRecordDelayCheckTask(Long lessonId, Long sectionId) {
        log.info("从缓存中删除学习记录，lessonId:{},sectionId:{}", lessonId, sectionId);
        //1.组装大Key
        String key = String.format(REDIS_KEY_PREFIX, lessonId);
        //2.组装HashKey
        String hashKey = sectionId.toString();
        //3.发起删除
        redisTemplate.opsForHash().delete(key, hashKey);
    }

    public void handleDelayTask() {
        log.info("处理延时任务");
        while (flag) {
            try {
                //1.从延时任务队列中获取任务
                DelayTask<delayTaskData> delayTask = delayQueue.take();
                //2.从redis中查询实时的播放进度
                delayTaskData data = delayTask.getData();
                log.info("获取到延时任务，任务数据：{}", data);
                //3.比较延时任务中的数据和redis中的进度是否一致
                LearningRecord redisRecord = readLearningRecordToCache(data.getLessonId(), data.getSectionId());
                //3.1、如果不一致，代表正在观看，无需处理，直接跳过
                if (ObjectUtil.isEmpty(redisRecord) || !data.getMoment().equals(redisRecord.getMoment())) {
                    continue;
                }
                //3.2、如果一致，代表已经没有观看，将redis数据更新到数据库中
                //3.2.1、更新学习记录表（moment数据）
                redisRecord.setFinished(null);
                recordMapper.updateById(redisRecord);
                //3.2.2、更新课表
                LearningLesson lesson = new LearningLesson();
                lesson.setId(data.getLessonId());
                lesson.setLatestSectionId(data.getSectionId());
                lesson.setLatestLearnTime(LocalDateTime.now().minusSeconds(20));
                lessonMapper.updateById(lesson);
            } catch (InterruptedException e) {
                log.error("处理延时任务失败", e);
            }
        }
    }

    @Data
    class RedisRecordData {
        private Long id;
        private Integer moment;
        private Boolean finished;

        public RedisRecordData(LearningRecord record) {
            this.id = record.getId();
            this.moment = record.getMoment();
            this.finished = record.getFinished();
        }
    }

    @Data
    class delayTaskData {
        private Long lessonId;
        private long sectionId;
        private Integer moment;

        public delayTaskData(LearningRecord record) {
            this.lessonId = record.getLessonId();
            this.moment = record.getMoment();
            this.sectionId = record.getSectionId();
        }
    }
}
